#include "../include/crypt_7th.h"
#include "../include/standard_library.h"
#include <string.h>

#define MAX_CHUNCK (1 << 13)

int sha256_fd_EVP_D(int fd, unsigned char *md_value);
int sha256_fd_EVP_DU(int fd, unsigned char *md_value);

int cryptPasswd(char *userpassd, char **salt, char **hash) {
	*salt = crypt_gensalt("$5$", 0, NULL, 0);
	if(*salt == NULL)
		return -1;
	*hash = crypt(userpassd, *salt);
	return 0;
}

void printHash(unsigned char *hash, size_t len) {
	for(size_t i = 0; i < len; i++) {
		printf("%02x", hash[i]);
	}
	printf("\n");
}

int getFileSha256(int file_fd, unsigned char *hash) {
	struct stat st;
	if(fstat(file_fd, &st))
		return -1;
	if(st.st_size < MAX_CHUNCK)
		return sha256_fd_EVP_D(file_fd, hash);
	return sha256_fd_EVP_DU(file_fd, hash);
}

int sha256_fd_EVP_D(int fd, unsigned char *md_value) {
	struct stat st;
	fstat(fd, &st);
	off_t file_size = st.st_size;
	// 验证文件大小
	if(file_size <= 0 || file_size > MAX_CHUNCK) {
		return -1;
	}
	// 读取文件内容
	unsigned char file_data[file_size];
	ssize_t bytes_read = read(fd, file_data, file_size);
	if(bytes_read != file_size) {
		perror("读取文件失败");
		return -1;
	}
	// 一次性计算哈希
	if(EVP_Digest(file_data, file_size, md_value, NULL, EVP_sha256(), NULL) !=
	   1) {
		return -1;
	}
	return 0;
}

int sha256_fd_EVP_DU(int fd, unsigned char *md_value) {
	struct stat sb;
	if(fstat(fd, &sb) == -1) {
		return -1;
	}
	// 如果文件大小为0，特殊处理
	if(sb.st_size == 0) {
		return -1;
	}
	EVP_MD_CTX *mdctx = EVP_MD_CTX_new();
	const EVP_MD *md = EVP_sha256();
	unsigned int md_len;
	EVP_DigestInit_ex(mdctx, md, NULL);

	// 6. 分块处理文件
	size_t remaining = sb.st_size;
	size_t offset = 0;
	void *addr;
	size_t this_chunk;
	while(remaining > 0) {
		this_chunk = remaining > MAX_CHUNCK ? MAX_CHUNCK : remaining;
		addr = mmap(NULL, this_chunk, PROT_READ, MAP_PRIVATE, fd, offset);

		if(1 != EVP_DigestUpdate(mdctx, (unsigned char *)addr, this_chunk)) {
			return -1;
		}

		munmap(addr, this_chunk);
		offset += this_chunk;
		remaining -= this_chunk;
	}
	EVP_DigestFinal_ex(mdctx, md_value, &md_len);

	EVP_MD_CTX_free(mdctx);
	munmap(addr, this_chunk);
	return 0;
}

int getFileSha256_str(int file_fd, char res_str[]) {

	unsigned char hash[SHA256_LENGTH];

	if(getFileSha256(file_fd, hash))
		return -1;
	for(int i = 0; i < SHA256_LENGTH; i++) {
		sprintf(&res_str[i], "%02x", hash[i]);
	}
	return 0;
}

int getToken(char name[], char token[]) {
	unsigned char hash[SHA256_LENGTH];
	if(EVP_Digest(name, strlen(name), hash, NULL, EVP_sha256(), NULL) !=
	   1) {
		return -1;
	}
	for(int i = 0; i < SHA256_LENGTH; i++) {
		sprintf(&token[i], "%02x", hash[i]);
	}
	return 0;
}

int getRandCryptSalt(char **salt) {
	*salt = crypt_gensalt("$5$", 0, NULL, 0);
	if(*salt == NULL)
		return -1;
	return 0;
}